<!DOCTYPE html>
<html>
  <head>
    <title>cannon.js - RigidVehicle</title>
    <meta charset="utf-8">
    <link rel="stylesheet" href="css/style.css" type="text/css"/>
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
  </head>
  <body>
    <script src="../build/cannon.js"></script>
    <script src="../build/cannon.demo.js"></script>
    <script src="../libs/dat.gui.js"></script>
    <script src="../libs/Three.js"></script>
    <script src="../libs/TrackballControls.js"></script>
    <script src="../libs/Detector.js"></script>
    <script src="../libs/Stats.js"></script>
    <script src="../libs/smoothie.js"></script>
    <script>

        var demo = new CANNON.Demo();
        var mass = 1;
        var vehicle;

        demo.addScene("car",function(){
            var world = demo.getWorld();
            world.gravity.set(0, 0, -30);
            world.broadphase = new CANNON.SAPBroadphase(world);
            world.defaultContactMaterial.friction = 0.2;

            var groundMaterial = new CANNON.Material("groundMaterial");
            var wheelMaterial = new CANNON.Material("wheelMaterial");
            var wheelGroundContactMaterial = window.wheelGroundContactMaterial = new CANNON.ContactMaterial(wheelMaterial, groundMaterial, {
                friction: 0.3,
                restitution: 0,
                contactEquationStiffness: 1000
            });

            // We must add the contact materials to the world
            world.addContactMaterial(wheelGroundContactMaterial);

            var chassisShape;
            var centerOfMassAdjust = new CANNON.Vec3(0, 0, -1);
            chassisShape = new CANNON.Box(new CANNON.Vec3(5, 2, 0.5));
            var chassisBody = new CANNON.Body({ mass: 1 });
            chassisBody.addShape(chassisShape, centerOfMassAdjust);
            chassisBody.position.set(0, 0, 0);

            // Create the vehicle
            vehicle = new CANNON.RigidVehicle({
                chassisBody: chassisBody
            });

            var axisWidth = 7;
            var wheelShape = new CANNON.Sphere(1.5);
            var down = new CANNON.Vec3(0, 0, -1);

            var wheelBody = new CANNON.Body({ mass: mass, material: wheelMaterial });
            wheelBody.addShape(wheelShape);
            vehicle.addWheel({
                body: wheelBody,
                position: new CANNON.Vec3(5, axisWidth/2, 0).vadd(centerOfMassAdjust),
                axis: new CANNON.Vec3(0, 1, 0),
                direction: down
            });

            var wheelBody = new CANNON.Body({ mass: mass, material: wheelMaterial });
            wheelBody.addShape(wheelShape);
            vehicle.addWheel({
                body: wheelBody,
                position: new CANNON.Vec3(5, -axisWidth/2, 0).vadd(centerOfMassAdjust),
                axis: new CANNON.Vec3(0, -1, 0),
                direction: down
            });

            var wheelBody = new CANNON.Body({ mass: mass, material: wheelMaterial });
            wheelBody.addShape(wheelShape);
            vehicle.addWheel({
                body: wheelBody,
                position: new CANNON.Vec3(-5, axisWidth/2, 0).vadd(centerOfMassAdjust),
                axis: new CANNON.Vec3(0, 1, 0),
                direction: down
            });

            var wheelBody = new CANNON.Body({ mass: mass, material: wheelMaterial });
            wheelBody.addShape(wheelShape);
            vehicle.addWheel({
                body: wheelBody,
                position: new CANNON.Vec3(-5, -axisWidth/2, 0).vadd(centerOfMassAdjust),
                axis: new CANNON.Vec3(0, -1, 0),
                direction: down
            });

            // Some damping to not spin wheels too fast
            for(var i=0; i<vehicle.wheelBodies.length; i++){
                vehicle.wheelBodies[i].angularDamping = 0.4;
            }

            // Constrain wheels
            var constraints = [];

            // Add visuals
            demo.addVisual(vehicle.chassisBody);
            for(var i=0; i<vehicle.wheelBodies.length; i++){
                demo.addVisual(vehicle.wheelBodies[i]);
            }

            vehicle.addToWorld(world);

            if(false){
                // Ground
                var groundShape = new CANNON.Plane();
                var ground = new CANNON.Body({ mass: 0, material: groundMaterial });
                ground.addShape(groundShape);
                ground.position.z = -3;
                world.addBody(ground);
                demo.addVisual(ground);
            } else {
                var mock = false;
                var matrix = [];
                var sizeX = 64,
                    sizeY = sizeX;

                for (var i = 0; i < sizeX; i++) {
                    matrix.push([]);
                    for (var j = 0; j < sizeY; j++) {
                        var height = Math.sin(i / sizeX * Math.PI * 8) * Math.sin(j / sizeY * Math.PI * 8) * 8 + 8;
                        if(i===0 || i === sizeX-1 || j===0 || j === sizeY-1)
                            height = 10;

                        matrix[i].push(height);
                    }
                }

                var hfShape = new CANNON.Heightfield(matrix, {
                    elementSize: 300 / sizeX
                });
                var hfBody;

                var quat = new CANNON.Quaternion();
                var pos = new CANNON.Vec3(-sizeX * hfShape.elementSize / 2, -20, -20);

                // Use normal
                hfBody = new CANNON.Body({ mass: 0, material: groundMaterial });
                hfBody.addShape(hfShape, new CANNON.Vec3(0,0,-1), new CANNON.Quaternion());
                hfBody.position.copy(pos);
                hfBody.quaternion.copy(quat);

                if(!mock){
                    world.addBody(hfBody);
                    demo.addVisual(hfBody);
                }

                if(mock){
                    for(var i=0; i<sizeX - 1; i++){
                        for (var j = 0; j < sizeY - 1; j++) {
                            for (var k = 0; k < 2; k++) {
                                hfShape.getConvexTrianglePillar(i, j, !!k);
                                var convexBody = new CANNON.Body({ mass: 0, material: groundMaterial });
                                convexBody.addShape(hfShape.pillarConvex);
                                hfBody.pointToWorldFrame(hfShape.pillarOffset, convexBody.position);
                                world.addBody(convexBody);
                                demo.addVisual(convexBody);
                            }
                        }
                    }
                }
            }
        });

        demo.start();

        document.onkeydown = handler;
        document.onkeyup = handler;

        var maxSteerVal = Math.PI / 8;
        var maxSpeed = 10;
        var maxForce = 100;
        function handler(event){
            var up = (event.type == 'keyup');

            if(!up && event.type !== 'keydown')
                return;

            switch(event.keyCode){

            case 38: // forward
                vehicle.setWheelForce(up ? 0 : maxForce, 2);
                vehicle.setWheelForce(up ? 0 : -maxForce, 3);
                break;

            case 40: // backward
                vehicle.setWheelForce(up ? 0 : -maxForce/2, 2);
                vehicle.setWheelForce(up ? 0 : maxForce/2, 3);
                break;

            case 39: // right
                vehicle.setSteeringValue(up ? 0 : -maxSteerVal, 0);
                vehicle.setSteeringValue(up ? 0 : -maxSteerVal, 1);
                break;

            case 37: // left
                vehicle.setSteeringValue(up ? 0 : maxSteerVal, 0);
                vehicle.setSteeringValue(up ? 0 : maxSteerVal, 1);
                break;

            }
        }

    </script>
  </body>
</html>
